package com.anxin.act.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.anxin.act.busFlow.entity.WfBusNode;
import com.anxin.act.busFlow.service.WfBusNodeService;
import com.anxin.act.config.dao.WfApplyFormDao;
import com.anxin.act.config.entity.WfApplyForm;
import com.anxin.act.config.service.WfApplyFormService;
import com.anxin.act.model.vo.ActModelVo;
import com.anxin.act.task.addTask.RuntimeActivityCreator;
import com.anxin.act.task.addTask.RuntimeActivityDefinitionEntity;
import com.anxin.act.task.addTask.SimpleRuntimeActivityDefinitionEntity;
import com.anxin.act.task.dao.WfActivityCreationDao;
import com.anxin.act.task.entity.ViewHiTask;
import com.anxin.act.task.service.ActTaskService;
import com.anxin.act.task.vo.FormPropertyVo;
import com.anxin.common.utils.LogUtils;
import com.anxin.common.utils.StringUtils;
import com.anxin.common.utils.spring.SpringUtils;
import com.anxin.desform.dao.OnlCgformHeadDao;
import com.anxin.desform.entity.OnlCgformHead;
import com.anxin.framework.web.entity.AjaxResult;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.RuntimeServiceImpl;
import org.activiti.engine.impl.bpmn.behavior.ParallelGatewayActivityBehavior;
import org.activiti.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.HistoricTaskInstanceEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.delegate.SignallableActivityBehavior;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: liuxiangyu
 * @date: 2020/3/30 18:30
 */
public class ActUtils {

    private static Logger logger = LoggerFactory.getLogger("sys-error");


    private static RuntimeService runtimeService = SpringUtils.getBean(RuntimeService.class);
    private static HistoryService historyService = SpringUtils.getBean(HistoryService.class);
    private static RepositoryService repositoryService = SpringUtils.getBean(RepositoryService.class);
    private static WfApplyFormService wfApplyFormService = SpringUtils.getBean(WfApplyFormService.class);
    private static TaskService taskService = SpringUtils.getBean(TaskService.class);
    private static WfBusNodeService wfBusNodeService = SpringUtils.getBean(WfBusNodeService.class);
    private static ProcessEngine processEngine = SpringUtils.getBean(ProcessEngine.class);
    private static WfActivityCreationDao wfActivityCreationDao = SpringUtils.getBean(WfActivityCreationDao.class);
    private static WfApplyFormDao wfApplyFormDao = SpringUtils.getBean(WfApplyFormDao.class);
    private static ActTaskService actTaskService = SpringUtils.getBean(ActTaskService.class);
    private static OnlCgformHeadDao onlCgformHeadDao = SpringUtils.getBean(OnlCgformHeadDao.class);


    /**
     * 根据流程实例ID获取流程变量
     * @param procInsId 流程实例ID
     * @return
     */
    public static Map<String,Object> getProcessVar(String procInsId){
        ProcessInstance proIns = runtimeService.createProcessInstanceQuery().processInstanceId(procInsId).includeProcessVariables().singleResult();
        if (proIns != null){
            return proIns.getProcessVariables();
        }

        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(procInsId).includeProcessVariables().singleResult();
        if (historicProcessInstance != null){
            return historicProcessInstance.getProcessVariables();
        }

//        Map processVar = redisCache.get("process_var_" + procInsId, Map.class);
//        if (processVar != null){
//            return processVar;
//        }

        return new HashMap<>();

    }

    /**
     * 根据流程实例ID获取流程的BusinessKey
     * @param procInsId 流程实例ID
     */
    public static String getBusinessKey(String procInsId){
        ProcessInstance proIns = runtimeService.createProcessInstanceQuery().processInstanceId(procInsId).includeProcessVariables().singleResult();
        if (proIns!=null) {

            return proIns.getBusinessKey();
        }
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(procInsId).includeProcessVariables().singleResult();
        if (historicProcessInstance != null){
            return historicProcessInstance.getBusinessKey();
        }
        return "";
    }

    /**
     * 根据流程实例ID获取分割好的流程的BusinessKey 0：业务表名  1：业务数据ID
     * @param procInsId 流程实例ID
     */
    public static String[] getBusinessKeys(String procInsId){
        return ActUtils.getBusinessKey(procInsId).split(":");
    }

    /**
     * 获取页面表名和业务ID
     * @param procInsId
     * @return
     */
    public static Map<String,String> getDataTableAndDataId(String procInsId){
        Map<String, Object> processVar = ActUtils.getProcessVar(procInsId);
        return ActUtils.getDataTableAndDataId(processVar);
    }

    /**
     * 获取业务ID
     * @param procInsId
     * @return
     */
    public static String getDataId(String procInsId){
        return ActUtils.getDataTableAndDataId(procInsId).get("actDataId");
    }


    /**
     * 获取页面表名和业务ID
     * @param executionEntity
     * @return
     */
    public static Map<String,String> getDataTableAndDataId(ExecutionEntity executionEntity){
        Map<String,Object> processVar = new HashMap<>();
        processVar.put("actDataId",executionEntity.getVariable("actDataId"));
        processVar.put("actDataTable",executionEntity.getVariable("actDataTable"));
        return ActUtils.getDataTableAndDataId(processVar);
    }

    /**
     * 获取启动流程所用的业务表名和业务ID
     * @param processVar 流程变量
     * @return
     */
    public static Map<String,String> getDataTableAndDataId(Map<String,Object> processVar){
        Map<String,String> resMap = new HashMap<>();
        String actDataId = processVar.get("actDataId").toString();
        String actDataTable = processVar.get("actDataTable").toString();


        if("wf_bus_node".equals(actDataTable)){
            WfBusNode wfBusNode = wfBusNodeService.get(actDataId);
            resMap.put("actDataId",wfBusNode.getDataId());
            resMap.put("actDataTable",ActUtils.getFormTable(wfBusNode.getFormType(),wfBusNode.getFormId()));
        }else{
            resMap.put("actDataId",actDataId);
            resMap.put("actDataTable",actDataTable);
        }
        return resMap;
    }


    public static String getFormTable(String formType,String formId){
        switch (formType){
            case "dev":{
                WfApplyForm wfApplyForm = wfApplyFormDao.getById(formId);
                return wfApplyForm.getTableName();
            }case "defined":{
                OnlCgformHead onlCgformHead = onlCgformHeadDao.getById(formId);
                return onlCgformHead.getTableName();
            }



        }
        return null;
    }

    /**
     * 获得流程定义对象
     * @param procDefId
     * @return
     */
    public static ProcessDefinition getProcessDefinition(String procDefId) {
        return repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId).singleResult();
    }

    /**
     * 根据流程定义的Key查询流程定义
     * @param procDefKey
     * @return
     */
    public static ProcessDefinition getProcessDefinitionByKey(String procDefKey){
        return  repositoryService.createProcessDefinitionQuery().processDefinitionKey(procDefKey).latestVersion().singleResult();
    }

    /**
     * 根据流程定义的key查询流程绑定的表单主键路径
     * @param procDefKey
     * @return
     */
    public static ActModelVo getActFormPathByProcDefKey(String procDefKey){

        //查询流程部署记录
        Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(ActUtils.getProcessDefinitionByKey(procDefKey).getDeploymentId()).singleResult();
        //查询模型-获取模型绑定的表单
        if (StringUtils.isNotBlank(deployment.getCategory())){
           return new ActModelVo(repositoryService.getModel(deployment.getCategory()));
        }
        return null;
    }

    /**
     * 获得流程定义对象的名称
     * @param procDefId
     * @return
     */
    public static String getProcessDefinitionName(String procDefId) {
        ProcessDefinition pd =  ActUtils.getProcessDefinition(procDefId);
        if (pd != null){
            return pd.getName();
        }
        return "";
    }

    /**
     * 根据表单类型和表单ID获取数据库表
     * @param formType
     * @param formId
     * @return
     */
    public static Map<String,Object> getTableNameAndPath(String formType,String formId){
        Map<String,Object> res = new HashMap<>();
        if ("dev".equals(formType)){
            WfApplyForm wfApplyForm = wfApplyFormService.get(formId);
            res.put("formTableName",wfApplyForm.getTableName());
            res.put("actFormPath",wfApplyForm.getPath());
            return res;
        }

        return res;
    }
    /**
     * 从数据库中加载加签节点
     * @param processDefinition
     * @param activityId
     * @return
     */
    public static ActivityImpl findAddTask(ProcessDefinitionEntity processDefinition,String activityId){
        return ActUtils.findAddTask(processDefinition,activityId,true);
    }

    /**
     * 从数据库中加载加签节点
     * @param processDefinition
     * @param activityId
     * @param initializeAll 是否初始化全部节点
     * @return
     */
    public static ActivityImpl findAddTask(ProcessDefinitionEntity processDefinition,String activityId,boolean initializeAll) {
        ActivityImpl resActivity = null;

        List<String> activityIdList = new ArrayList<>();


        try {
            RuntimeActivityDefinitionEntity entity = wfActivityCreationDao.getByCloneId(activityId);
            List<RuntimeActivityDefinitionEntity> entityList = new ArrayList<RuntimeActivityDefinitionEntity>();
            entityList.add(entity);
            activityIdList.add(activityId);



            String propertiesText = entity.getPropertiesText();
            JSONObject json = JSONObject.parseObject(propertiesText);
            String prototypeActivityId = json.getString("prototypeActivityId");

            while (StringUtils.isNotBlank(prototypeActivityId) && prototypeActivityId.split(":").length == 3 && !activityId.equals(prototypeActivityId)) {
                RuntimeActivityDefinitionEntity entity1 = wfActivityCreationDao.getByCloneId(prototypeActivityId);
                if (!activityIdList.contains(prototypeActivityId)){
                    entityList.add(entity1);
                    activityIdList.add(prototypeActivityId);
                }


                propertiesText = entity1.getPropertiesText();
                json = JSONObject.parseObject(propertiesText);

                JSONArray cloneActivityIds = json.getJSONArray("cloneActivityIds");


                prototypeActivityId = json.getString("prototypeActivityId");

                if (cloneActivityIds !=null && cloneActivityIds.size() > 0){
                    ProcessDefinitionEntity pde  = (ProcessDefinitionEntity)((RepositoryServiceImpl)repositoryService).getDeployedProcessDefinition(processDefinition.getId());
                    ActivityImpl activity = pde.findActivity(cloneActivityIds.get(0).toString());
                    if (activity != null){
                        continue;
                    }
                }

            }


            // 查询该流程的所有加签，找到相关的也加载到内存
            if (initializeAll){
                List<SimpleRuntimeActivityDefinitionEntity> entityListByActId = wfActivityCreationDao.getByProcessInstanceId(entity.getProcessInstanceId());
                for (SimpleRuntimeActivityDefinitionEntity simpleRuntimeActivityDefinitionEntity : entityListByActId) {
                    propertiesText = simpleRuntimeActivityDefinitionEntity.getPropertiesText();
                    json = JSONObject.parseObject(propertiesText);

                    if (activityId.equals(json.getString("prototypeActivityId"))){
                        if (!activityIdList.contains(prototypeActivityId)){
                            activityIdList.add(prototypeActivityId);
                            entityList.add(simpleRuntimeActivityDefinitionEntity);
                        }

                    }

                }
            }

            if (processDefinition != null) {
                for (int i = entityList.size() - 1; i >= 0; i--) {
                    try {
                        entity = entityList.get(i);
                        String factoryNam = entity.getFactoryName();
                        factoryNam = StringUtils.white_list(factoryNam);
                        RuntimeActivityCreator activitiesCreator = (RuntimeActivityCreator) Class.forName(factoryNam).newInstance();
                        entity.deserializeProperties();
                        ActivityImpl[] activities = activitiesCreator.createActivities(processEngine, processDefinition, entity, null);

                        if (resActivity == null){
                            resActivity = activities[0];
                        }
                    }catch (Exception e){
                        LogUtils.error("加签流程再次创建异常",e);
                    }

                }
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            logger.error("ActUtils--findAddTask 从数据库中加载加签节点异常", e);
        }

        return resActivity;
    }

    /**
     * 根据流程定义ID，和任务节点标识获取 ActivityImpl 对象
     * @param processDefId 根据流程定义ID
     * @param taskKey 任务节点标识
     * @return
     */
    public static ActivityImpl getActivityImpl(String processDefId, String taskKey){
        return ActUtils.getActivityImpl(processDefId,taskKey,false);
    }

    /**
     * 根据流程定义ID，和任务节点标识获取 ActivityImpl 对象
     * @param processDefId 根据流程定义ID
     * @param taskKey 任务节点标识
     * @param initializeAll 是否出初始化全部节点
     * @return
     */
    public static ActivityImpl getActivityImpl(String processDefId, String taskKey,boolean initializeAll){
        ProcessDefinitionEntity pde  = (ProcessDefinitionEntity)((RepositoryServiceImpl)repositoryService).getDeployedProcessDefinition(processDefId);
        ActivityImpl activity = pde.findActivity(taskKey);
        if (activity == null){
            activity = ActUtils.findAddTask(pde,taskKey,initializeAll);
        }
        return activity;
    }


    /**
     * 根据流程定义ID，和任务节点标识获取 ActivityImpl 对象
     * @param processDefId 根据流程定义ID
     * @param taskKey 任务节点标识
     * @return
     */
    public static ActivityImpl getActivityImplNoLoading(String processDefId, String taskKey){
        ProcessDefinitionEntity pde  = (ProcessDefinitionEntity)((RepositoryServiceImpl)repositoryService).getDeployedProcessDefinition(processDefId);
        ActivityImpl activity = pde.findActivity(taskKey);
        return activity;
    }


    /**
     * 根据流程定义ID，和任务节点标识获取 ActivityImpl 对象 (不去加载加签节点)
     * @param processDefId 根据流程定义ID
     * @param taskKey 任务节点标识
     * @return
     */
    public static ActivityImpl getActivity(String processDefId, String taskKey){
        ProcessDefinitionEntity pde  = (ProcessDefinitionEntity)((RepositoryServiceImpl)repositoryService).getDeployedProcessDefinition(processDefId);
        return pde.findActivity(taskKey);
    }

    /**
     * 根据task对象查询 ActivityImpl 对象
     * @param task
     * @return
     */
    public static ActivityImpl getActivityImpl(Task task){
        ProcessDefinitionEntity pde = (ProcessDefinitionEntity) ((RepositoryServiceImpl)repositoryService).getDeployedProcessDefinition(task.getProcessDefinitionId());
        return pde.findActivity(task.getTaskDefinitionKey());
    }

    /**
     * 获取审批人集合
     * @param taskId 待办ID
     * @return
     */
    public static List<String> getAssigneeByTask(String taskId){
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task != null){
            return ActUtils.getAssigneeByTask(task);
        }
        return new ArrayList<>();
    }

    /**
     * 获取审批人集合
     * @param viewHiTask
     * @return
     */
    public static List<String> getAssigneeByTask(ViewHiTask viewHiTask){
        List<String> userList = new ArrayList<String>();
        if (StringUtils.isNotBlank(viewHiTask.getAssignee())) {
            userList.add(viewHiTask.getAssignee());
        }else if("actTask".equals(viewHiTask.getType())){
            HistoricTaskInstance hiTask = ActTaskUtils.getHiTask(viewHiTask.getTaskId());
            return ActUtils.getAssigneeByTask(hiTask);
        }


        return userList;
    }

    /**
     * 获取审批人集合
     * @param hiTas
     * @return
     */
    public static List<String> getAssigneeByTask(HistoricTaskInstance hiTas ){
        List<String> userList = new ArrayList<String>();
        //当前节点审批人不空直接返回
        if (StringUtils.isNotBlank(hiTas.getAssignee())) {
            userList.add(hiTas.getAssignee());
        }else {
            if (hiTas instanceof HistoricTaskInstanceEntity){
//            if ("HistoricTaskInstanceEntity".equals(task.getClass().getSimpleName())){
                if (StringUtils.isNotBlank(((HistoricTaskInstanceEntity) hiTas).getDeleteReason())){
                    return userList;
                }
            }
            List<IdentityLink> cdd = taskService.getIdentityLinksForTask(hiTas.getId());
            for (IdentityLink identityLink : cdd) {
                //候选用户
                String cddUser = identityLink.getUserId();
                if(StringUtils.isNotBlank(cddUser) && !userList.contains(cddUser)){
                    userList.add(cddUser);
                }
            }
        }

        return userList;
    }



    /**
     * 获取审批人集合
     * @param task
     * @return
     */
    public static List<String> getAssigneeByTask(TaskInfo task){
        List<String> userList = new ArrayList<String>();
        //当前节点审批人不空直接返回
        if (StringUtils.isNotBlank(task.getAssignee())) {
            userList.add(task.getAssignee());
        }else {
            if (task instanceof HistoricTaskInstanceEntity){
//            if ("HistoricTaskInstanceEntity".equals(task.getClass().getSimpleName())){
                if (StringUtils.isNotBlank(((HistoricTaskInstanceEntity) task).getDeleteReason())){
                    return userList;
                }
            }
            List<IdentityLink> cdd = taskService.getIdentityLinksForTask(task.getId());
            for (IdentityLink identityLink : cdd) {
                //候选用户
                String cddUser = identityLink.getUserId();
                if(StringUtils.isNotBlank(cddUser) && !userList.contains(cddUser)){
                    userList.add(cddUser);
                }
            }
        }

        return userList;
    }

    /**
     * 代办设置审批人
     * @param taskId
     * @param assignee
     */
    public static void setAssignee(String taskId,String assignee){
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        ActUtils.setAssignee(task,assignee);
    }

    /**
     * 代办设置审批人
     * @param task
     * @param assignee
     */
    public static void setAssignee(Task task,String assignee){
        if (task.getAssignee() == null){
            ActTaskUtils.claimTask(task.getId(), assignee);
            taskService.setAssignee(task.getId(), assignee);
        }

    }


    /**
     * 判断是否审批通过
     * @param status 流程状态
     * @return
     */
    public static boolean isThrough(String status) {
        // W 为审批通过 H为豁免
        if ("W".equals(status) || "H".equals(status)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 获取审批意见字段值
     * @param formFieldList
     * @param fieldName
     * @return
     */
    public static String getFieldValue(List<FormPropertyVo> formFieldList,String fieldName){
        for (FormPropertyVo formPropertyVo : formFieldList) {
            if (fieldName.equals(formPropertyVo.getName())){
                return formPropertyVo.getValue();
            }
        }
        return "";
    }

    /**
     * 获取流程上一审批人
     * @param processInstanceId 流程实例ID
     * @return
     */
    public static String getLastAssignee(String processInstanceId){
        String assignee = null;
        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).activityType("userTask")
                .orderByHistoricActivityInstanceStartTime().asc().list();
        if (list.size()>0) {
            HistoricActivityInstance historicActivityInstance = list.get(list.size()-1);
            if (historicActivityInstance.getAssignee()!=null) {
                assignee=historicActivityInstance.getAssignee();
            }
        }else {
            assignee = ActUtils.getProcessVar(processInstanceId).get("applyUserName")!=null ? ActUtils.getProcessVar(processInstanceId).get("applyUserName").toString() : "";
        }

        return assignee;
    }

    /**
     * 流程流程的SignallableActivityBehavior 如果是加签流程则从原流程中获取
     * @param activity
     * @return
     */
    public static SignallableActivityBehavior getSignallableActivityBehavior(ActivityImpl activity){
        SignallableActivityBehavior activityBehavior = (SignallableActivityBehavior) activity.getActivityBehavior();
        if (activityBehavior == null && activity.getId().split(":").length == 3){
            activityBehavior = (SignallableActivityBehavior)(ActUtils.getActivityImpl(activity.getProcessDefinition().getId(), activity.getId().split(":")[1]).getActivityBehavior());
        }
        return activityBehavior;
    }

    /**
     * 获取流出的activity任务
     * @param processDefId 流程定义ID
     * @param taskKey 待办Key
     * @return
     */
    public static List<ActivityImpl> getOutgoingActivity(String processDefId, String taskKey){
        // 查询该节点之后的节点
        List<ActivityImpl> userTaskActList = new ArrayList<>();
        ActivityImpl activityImpl = ActUtils.getActivityImpl(processDefId, taskKey);
        ActUtils.getOutgoingActivity(userTaskActList, activityImpl);

        return userTaskActList;
    }


    /**
     * 获取流出的activity任务
     * @param userTaskActList
     * @param activityImpl
     * @return
     */
    public static void getOutgoingActivity(List<ActivityImpl> userTaskActList,ActivityImpl activityImpl){
        List<PvmTransition> outgoingTransitionsList = activityImpl.getOutgoingTransitions();
        for (PvmTransition pvmTransition : outgoingTransitionsList) {
            ActivityImpl activity = (ActivityImpl)pvmTransition.getDestination();
            if (activity.getActivityBehavior() instanceof UserTaskActivityBehavior || activity.getActivityBehavior() instanceof ParallelMultiInstanceBehavior){
                userTaskActList.add(activity);
            }else {
                // 并行网关也记录
                if (activity.getActivityBehavior() instanceof ParallelGatewayActivityBehavior){
                    userTaskActList.add(activity);
                }
                ActUtils.getOutgoingActivity(userTaskActList,activity);
            }
        }
    }

    /**
     * 校验任务是否可以驳回
     * @param taskId
     * @return
     */
    public static boolean rejectedCheck(String taskId){
        AjaxResult ajaxResult = actTaskService.rejectedCheck(taskId);
        return ajaxResult.getData() == null || StringUtils.isBlank(ajaxResult.getData().toString());
    }



//    /**
//     *
//     * @param currentTaskEntity 当前活动的任务
//     * @param activity 跳转到的任务
//     */
//    public static void moveTo(TaskEntity currentTaskEntity, ActivityImpl activity) {
//        executeCommand(new StartActCmd(currentTaskEntity.getExecutionId(), activity));
//        executeCommand(new DeleteRuTaskCmd(currentTaskEntity));
//    }

    public static void executeCommand(Command<Void> command) {
        ((RuntimeServiceImpl) runtimeService).getCommandExecutor().execute(command);
    }



}
